package com.zlzlib.log.service.impl;

import com.zlzlib.log.bean.LogConstants;
import com.zlzlib.log.bean.vo.LogStatusVo;
import com.zlzlib.log.service.LogSaveService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.stereotype.Service;

import javax.annotation.PreDestroy;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author zlz
 * @version 1.0
 * @dateTime: 2025/7/1 15:31
 */
@Service
@EnableAsync
public class LogSaveServiceImpl implements LogSaveService {

    private static class LogWrapper {
        final Object log;
        final String collectionName;

        LogWrapper(Object log, String collectionName) {
            this.log = log;
            this.collectionName = collectionName;
        }
    }

    private final BlockingQueue<LogWrapper> logQueue = new LinkedBlockingQueue<>(10000);
    private final MongoTemplate mongoTemplate;
    private volatile boolean isShuttingDown = false;
    /**
     * 每次取出数据最大长度
     */
    @Value("${log.batch.size.max:1000}")
    private int maxBatchSize;
    /**
     * 每次分批存到mongodb的数量长度 要根据日志大小进行配置 如果太大保存会失败
     */
    @Value("${log.batch.size.per-collection:200}")
    private int batchSizePerCollection;
    /**
     * 每次存入的时间间隔
     */
    @Value("${log.flush.interval:5000}")
    private int flushInterval;
    /**
     * 如果日志缓存数量超过此值则加快存入间隔 为1秒
     */
    @Value("${log.queue.warn-threshold:8000}")
    private int queueWarnThreshold;
    /**
     * 如果日志缓存数量超过此值则加快存入间隔 为0.5秒
     */
    @Value("${log.queue.critical-threshold:9500}")
    private int queueCriticalThreshold;

    /**
     * 监控指标
     */
    private final AtomicInteger queueFullCount = new AtomicInteger(0);
    private final AtomicInteger insertErrorCount = new AtomicInteger(0);
    private final AtomicLong totalSavedCount = new AtomicLong(0);
    private final AtomicLong lastFlushTime = new AtomicLong(System.currentTimeMillis());

    @Autowired
    public LogSaveServiceImpl(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
        initFlushThread();
    }

    @Override
    @Async(LogConstants.SAVE_LOG_NAME)
    public void saveLog(Object po) {
        addLog(po, LogConstants.LOG_COLLECTION_NAME);
    }

    @Override
    @Async(LogConstants.SAVE_LOG_NAME)
    public void saveLog(String fileName, Object po) {
        addLog(po, fileName);
    }

    @Override
    public LogStatusVo getStatus() {
        LogStatusVo vo = new LogStatusVo();
        vo.setQueueSize(logQueue.size());
        vo.setQueueFullCount(queueFullCount.get());
        vo.setInsertErrorCount(insertErrorCount.get());
        vo.setTotalSavedCount(totalSavedCount.get());
        vo.setLastTime(new Date(lastFlushTime.get()));
        vo.setShuttingDown(isShuttingDown);
        return vo;
    }

    /**
     * 添加日志到队列（指定集合名称）
     *
     * @param log            日志
     * @param collectionName 名称
     */
    private void addLog(Object log, String collectionName) {
        if (log == null || collectionName == null || collectionName.trim().isEmpty()) {
            return;
        }

        if (!isShuttingDown) {
            if (!logQueue.offer(new LogWrapper(log, collectionName))) {
                handleQueueFull(log, collectionName);
            }
        } else {
            // 关闭时直接写入
            saveDirectly(log, collectionName);
        }
    }

    /**
     * 初始化刷盘线程
     */
    private void initFlushThread() {
        Thread flushThread = new Thread(() -> {
            while (!isShuttingDown || !logQueue.isEmpty()) {
                try {
                    // 动态调整刷新间隔
                    int sleepTime = calculateFlushInterval();
                    Thread.sleep(sleepTime);
                    flushLogsToDb();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
        flushThread.setDaemon(true);
        flushThread.setName("Log-Flush-Thread");
        flushThread.start();
    }

    /**
     * @return 动态计算刷新间隔
     */
    private int calculateFlushInterval() {
        int size = logQueue.size();

        // 高负载时加快刷新频率
        if (size > queueCriticalThreshold) {
            // 0.5秒
            return 500;
        } else if (size > queueWarnThreshold) {
            // 1秒
            return 1000;
        }
        // 默认配置
        return flushInterval;
    }

    /**
     * 批量写入数据库（按集合分组）
     */
    private void flushLogsToDb() {
        List<LogWrapper> entries = new ArrayList<>();
        logQueue.drainTo(entries, maxBatchSize);

        if (!entries.isEmpty()) {
            // 按集合名称分组
            Map<String, List<Object>> logsByCollection = new HashMap<>();
            for (LogWrapper wrapper : entries) {
                logsByCollection
                        .computeIfAbsent(wrapper.collectionName, k -> new ArrayList<>())
                        .add(wrapper.log);
            }
            // 分批写入不同集合
            for (Map.Entry<String, List<Object>> entry : logsByCollection.entrySet()) {
                String collectionName = entry.getKey();
                List<Object> logs = entry.getValue();

                try {
                    // 每个集合的日志再分成小批次
                    int total = logs.size();
                    for (int i = 0; i < total; i += batchSizePerCollection) {
                        int end = Math.min(i + batchSizePerCollection, total);
                        List<Object> batch = logs.subList(i, end);
                        mongoTemplate.insert(batch, collectionName);
                    }
                    totalSavedCount.addAndGet(logs.size());
                } catch (Exception e) {
                    handleInsertError(collectionName, logs, e);
                }
            }
            // 记录最后刷新时间
            lastFlushTime.set(System.currentTimeMillis());
        }
    }


    private void handleQueueFull(Object log, String collectionName) {
        // 队列满时的处理
        queueFullCount.incrementAndGet();
        // 直接写入数据库（牺牲性能保证不丢失）
        saveDirectly(log, collectionName);
    }


    private void saveDirectly(Object log, String collectionName) {
        // 直接保存单条日志
        try {
            mongoTemplate.insert(log, collectionName);
            totalSavedCount.incrementAndGet();
        } catch (Exception ex) {
            insertErrorCount.incrementAndGet();
        }
    }


    private void handleInsertError(String collectionName, List<Object> logs, Exception e) {
        // 插入错误处理
        insertErrorCount.incrementAndGet();
        // 重试策略
        try {
            Thread.sleep(100);
            mongoTemplate.insert(logs, collectionName);
            totalSavedCount.addAndGet(logs.size());
        } catch (Exception ex) {
            // 持久化到文件
        }
    }



    @PreDestroy
    public void onShutdown() {
        isShuttingDown = true;
        // 强制刷出剩余日志
        flushLogsToDb();
    }
}
